﻿using System;
using System.Collections.Generic;
using MugenInjection.Delegates;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;

// ReSharper disable CheckNamespace

namespace MugenInjection.Core.Components
// ReSharper restore CheckNamespace
{
    /// <summary>
    ///     Represents the dynamic converter to adapt a specific service.
    /// </summary>
    public class AdapterDynamicConverter : IDynamicConverter
    {
        #region Fields

        private readonly Func<IBindingContext, object, object> _adapter;
        private readonly CanResolveBindingDelegate _canResolveDelegate;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="AdapterDynamicConverter" /> class.
        /// </summary>
        public AdapterDynamicConverter(Type typeFrom, Type typeTo, Func<IBindingContext, object, object> adapter,
                                       CanResolveBindingDelegate canResolveDelegate)
        {
            Validate.ArgumentNotNull(typeFrom, "typeFrom");
            Validate.ArgumentNotNull(typeTo, "typeTo");
            Validate.ArgumentNotNull(adapter, "adapter");
            Validate.ArgumentNotNull(canResolveDelegate, "canResolveDelegate");
            _adapter = adapter;
            _canResolveDelegate = canResolveDelegate;
            TypeFrom = typeFrom;
            TypeTo = typeTo;
            Services = new[] { typeTo };
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets the type from.
        /// </summary>
        public Type TypeFrom { get; set; }

        /// <summary>
        ///     Gets the type to.
        /// </summary>
        public Type TypeTo { get; set; }

        #endregion

        #region Implementation of IDynamicConverter

        /// <summary>
        ///     Gets the service types.
        /// </summary>
        public IList<Type> Services { get; private set; }

        /// <summary>
        ///     Determines whether the specified request can be resolved.
        /// </summary>
        /// <param name="bindingContext">
        ///     The specified <see cref="IBindingContext" />.
        /// </param>
        /// <param name="convertType">The type to convert.</param>
        /// <returns>
        ///     <c>True</c> if the specified service has been resolved; otherwise, <c>false</c>.
        /// </returns>
        public bool CanResolve(IBindingContext bindingContext, out Type convertType)
        {
            if (_canResolveDelegate(bindingContext))
            {
                convertType = TypeTo;
                return true;
            }
            convertType = null;
            return false;
        }

        /// <summary>
        ///     Resolves instance for the specified <see cref="IBindingContext" />.
        /// </summary>
        /// <param name="context">
        ///     The specified <see cref="ConverterContext" />.
        /// </param>
        /// <returns>An instance of the service.</returns>
        public object Resolve(ConverterContext context)
        {
            var bindingContext = context.BindingContext;
            Type oldService = bindingContext.Service;
            bindingContext.Service = TypeFrom;
            object resolve = bindingContext.CallInjector.Resolve(bindingContext);
            bindingContext.Service = oldService;
            return _adapter(bindingContext, resolve);
        }

        #endregion
    }
}